<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>函数高级拓展</title>
    <style>
        body {
            background-color: #000;
        }
    </style>
</head>

<body>

    <script src="./index.js"></script>

    <!-- 
        1.递归函数：
            递归：每次递出去 都要拿回来； 来源于数学归纳法： 顺推发 | 逆推法；

            思想：将一个复杂问题分解成多个简单的问题 然后重复下去并解决掉；

            我们需要将问题 抽象成递归的问题，然后写函数去解决它，可用于简化 for 循环 提高运算效率；

            递归函数的三要素：
                1.结束条件;
                2.通项公式；
                3.函数自调用本身；

                例如 求 1~100 的和
                func f(n){

                    结束条件
                    if(n === 1) return 1

                    递归过程
                    return f(n - 1) + n
                }

                但是这样的写法很糟糕，当运算量很大的时候，
                运算强度会增加 时间复杂度会大大增加 运算效率会很低；

                比如： 斐波那契数列的 第 n 项
                1 1 2 3 5 8 13 21... 
                f(1) = f(2) = 1
                f(n) = f(n-1) + f(n - 2)

                function fibonacci(n) {
                    if (n < 3) return 1 
                    return fibonacci(n - 1) + fibonacci(n - 2) 
                }

                fibonacci(50) .... 运算时间非常之大...

                这样的写法 运算强度非常大 时间复杂度很高，就是个垃圾函数：
                垃圾的原因：
                    f(50) = f(49) + f(48)
                    f(49) = f(48) + f(47)...

                    f(50) = f(48) + f(48) + f(47) ...
                    可见， f(48) 被算了两次...
                    因此 每一项的计算，都要把之前算过的项 再算一遍，耗费了更多的时间...

                如何解决这个问题：
                    我们需要给递归函数添加记忆功能...
                    把之前计算过的结果保存起来，用到就直接查询 而不必再去重新计算...
                    通常 都是利用对象或者数组 来保存计算结果...

                    let list = {}
                    function fibonacci(n) {
                        如果计算过这一项 就直接查表
                        if(list[n]) return list[n]

                        结束条件
                        if (n < 3){
                            list[n] = 1
                            return list[n]
                        } 

                        没计算过就计算一下 然后保存起来
                        list[n] = fibonacci(n - 1) + fibonacci(n - 2)
                        return list[n]
                }

                这样运算效率就会非常高；
                还有一种解决方法：
                    利用函数自身的属性，函数本身也是个全局对象 也可以用来保存计算结果
                    当然，这里可以使用 arguments.callee 函数指针来回调自己，
                    这样，函数名被更改了，也不会影响功能；

                   function fibonacciPlus(n) {

                        if (fibonacciPlus[n]) return fibonacciPlus[n]

                        if (n < 3) { 
                            fibonacciPlus[n]=1 
                            return fibonacciPlus[n] 
                        } 
                        使用arguments 即使函数名更改了 依然可以执行
                    
                        在函数自身上面 保存计算结果
                        fibonacciPlus[n] = arguments.callee(n - 1) + arguments.callee(n - 2) 
                    
                        return fibonacciPlus[n] 
                   }

        2.纯函数：
            函数的一种：有输入 有输出，中间不会修改任何全局状态；
                func f(x){
                    return x*x
                }
            通常就是用于 运算一个结果 或者输出某种格式...
            建议多写纯函数，这样在后续的删改增的操作中 对全局的影响不会很大；

        3.偏函数：
            如果一个函数接收若干参数，可以写成偏函数，让函数分批次传参；
            func f(x,y){
                return x + y
            }

            改写为
            func f(x){
                return ff(y){
                    return x + y
                }
            }

            f(2)(3) = 5

            本质山 也是用闭包来保存外部函数传入的参数；
     -->
</body>

</html>